package jdos.hardware;

import java.util.Arrays;

import jdos.Dosbox;
import jdos.cpu.Paging;
import jdos.hardware.VGA.VGA_Latch;
import jdos.ints.Int10_modes;
import jdos.misc.Log;
import jdos.types.LogSeverities;
import jdos.types.LogTypes;
import jdos.util.Ptr;

public class VGA_memory {

	/* Gonna assume that whoever maps vga memory, maps it on 32/64kb boundary */


	final public class VRAM {
		public static int[] direct = new int[1024 * 256];

		public static /*Bit8u*/short readb(/*HostPt*/int address) {
			return (short) ((direct[(address >>> 2)] >>> ((address & 0x3) << 3)) & 0xFF);
		}
	    public static /*Bit32u*/int readd(/*HostPt*/int address) {
            return direct[address >>> 2];
	    }

		public static void writeb(/*HostPt*/int address, /*Bit8u*/ int value) {
			int off = (address & 0x3) << 3;
			int[] local = direct;
			int mask = ~(0xFF << off);
			int index = (address >>> 2);
			int val = local[index] & mask | (value & 0xFF) << off;
			local[index] = val;
		}

	}

    public static VGA_Latch latch = new VGA_Latch();
    
	public static int readHandler(/*PhysPt*/int start) {
		latch.d = VRAM.readd(start * 4);

		if (VGA.newType) {
			int a = 0;
			a |= ((latch.d >> (0 + (VGA_gfx.read_map_select))) & 1) << 7;
			a |= ((latch.d >> (4 + (VGA_gfx.read_map_select))) & 1) << 6;
			a |= ((latch.d >> (8 + (VGA_gfx.read_map_select))) & 1) << 5;
			a |= ((latch.d >> (12 + (VGA_gfx.read_map_select))) & 1) << 4;
			a |= ((latch.d >> (16 + (VGA_gfx.read_map_select))) & 1) << 3;
			a |= ((latch.d >> (20 + (VGA_gfx.read_map_select))) & 1) << 2;
			a |= ((latch.d >> (24 + (VGA_gfx.read_map_select))) & 1) << 1;
			a |= ((latch.d >> (28 + (VGA_gfx.read_map_select))) & 1) << 0;
			return a;
		} else {
			if (VGA.vga.config.read_mode == 0) {
				return (latch.b(VGA_gfx.read_map_select));
			} else {
				VGA.VGA_Latch templatch = new VGA.VGA_Latch();
				templatch.d = (latch.d & VGA.FillTable[VGA.vga.config.color_dont_care]) ^ VGA.FillTable[VGA.vga.config.color_compare & VGA.vga.config.color_dont_care];
				return (~(templatch.b(0) | templatch.b(1) | templatch.b(2) | templatch.b(3))) & 0xFF;
			}
		}
	}
    public static int readbB8(/*PhysPt*/int addr) {
    	return VRAM.readb(addr+0x30000);
    }
    public static int readbA0(/*PhysPt*/int addr) {
        return readHandler(addr);
    }

    //Nice one from DosEmu
    static private /*Bit32u*/int RasterOp(/*Bit32u*/int input,/*Bit32u*/int mask) {
        switch (VGA.vga.config.raster_op) {
        case 0x00:	/* None */
            return (input & mask) | (latch.d & ~mask);
        case 0x01:	/* AND */
            return (input | ~mask) & latch.d;
        case 0x02:	/* OR */
            return (input & mask) | latch.d;
        case 0x03:	/* XOR */
            return (input & mask) ^ latch.d;
        }
        return 0;
    }

    static private /*Bit32u*/int ModeOperation(/*Bit8u*/int val) {
        /*Bit32u*/int full;
        int full_set_reset=VGA.FillTable[VGA_gfx.set_reset];
        int full_bit_mask=VGA.ExpandTable[VGA_gfx.bit_mask];
        
        switch (VGA.vga.config.write_mode) {
        case 0x00:
            // Write Mode 0: In this mode, the host data is first rotated as per the Rotate Count field, 
        	//then the Enable Set/Reset mechanism selects data from this or the Set/Reset field. 
        	//Then the selected Logical Operation is performed on the resulting data and the data 
        	//in the latch register. Then the Bit Mask field is used to select which bits come from 
        	//the resulting data and which come from the latch register. Finally, only the bit planes 
        	//enabled by the Memory Plane Write Enable field are written to memory.
            full=VGA.ExpandTable[val];
            int full_enable_set_reset=VGA.FillTable[VGA_gfx.enable_set_reset];
            int full_enable_and_set_reset=full_set_reset & full_enable_set_reset;
            int full_not_enable_set_reset=~full_enable_set_reset;
            full=(full & full_not_enable_set_reset) | full_enable_and_set_reset;
            full=RasterOp(full,full_bit_mask);
            break;
        case 0x01:
            // Write Mode 1: In this mode, data is transferred directly from the 32 bit latch register to display memory, 
        	//affected only by the Memory Plane Write Enable field. The host data is not used in this mode.
            full=latch.d;
            break;
        case 0x02:
            //Write Mode 2: In this mode, the bits 3-0 of the host data are replicated across all 8 bits of their respective planes.
        	//Then the selected Logical Operation is performed on the resulting data and the data in the latch register.
        	//Then the Bit Mask field is used to select which bits come from the resulting data and which come from the latch register.
        	//Finally, only the bit planes enabled by the Memory Plane Write Enable field are written to memory.
            full=RasterOp(VGA.FillTable[val&0xF],full_bit_mask);
            break;
        case 0x03:
            // Write Mode 3: In this mode, the data in the Set/Reset field is used as if the Enable Set/Reset field were set to 1111b.
        	//Then the host data is first rotated as per the Rotate Count field, then logical ANDed with the value of the Bit Mask field.
        	//The resulting value is used on the data obtained from the Set/Reset field in the same way that the Bit Mask field would ordinarily be used.
        	//to select which bits come from the expansion of the Set/Reset field and which come from the latch register. Finally, 
        	//only the bit planes enabled by the Memory Plane Write Enable field are written to memory.
            full=RasterOp(full_set_reset,VGA.ExpandTable[val] & full_bit_mask);
            break;
        default:
            if (Log.level<=LogSeverities.LOG_NORMAL) Log.log(LogTypes.LOG_VGAMISC, LogSeverities.LOG_NORMAL,"VGA:Unsupported write mode "+VGA.vga.config.write_mode);
            full=0;
            break;
        }
        return full;
    }
    //template< bool wrapping>
    public static void writeHandler(/*PhysPt*/int start, /*Bit8u*/int val) {
        /*Bit32u*/int data=ModeOperation(val & 0xFF);

        int full_map_mask=VGA.FillTable[VGA.vga.seq.map_mask];
        
        int full_not_map_mask=~full_map_mask;
        
        /* Update video memory and the pixel buffer */
        VGA.VGA_Latch pixels = new VGA.VGA_Latch();
        pixels.d=VRAM.readd(start*4);
        pixels.d&=full_not_map_mask;
        pixels.d|=(data & full_map_mask);
        VRAM.writeb(start*4+0, pixels.b(0));
        VRAM.writeb(start*4+1, pixels.b(1));
        VRAM.writeb(start*4+2, pixels.b(2));
        VRAM.writeb(start*4+3, pixels.b(3));

    }

	public static void writebB8(/*PhysPt*/int addr, /*Bitu*/int val) {
		VRAM.writeb(addr+0x30000, val);
	}
	public static void writebA0(/*PhysPt*/int addr, /*Bitu*/int val) {
			writeHandler(addr + 0, val);
	}


    static public void writeFont(/*PhysPt*/int addr,/*Bitu*/int val){
        VGA.vga.draw.font[addr]=(/*Bit8u*/byte)val;

    }
    
}
